<template>
  <div class="tree-transfer">
    <!-- 左侧树形面板 -->
    <div class="transfer-panel">
      <div class="panel-header">
        <el-checkbox
          v-model="leftAllChecked"
          :indeterminate="leftIndeterminate"
          @change="handleLeftAllChange"
        >
          {{ leftTitle }} ({{ leftCheckedCount }}/{{ leftTotalCount }})
        </el-checkbox>
      </div>
      <div class="panel-body">
        <el-tree
          ref="leftTree"
          :data="leftData"
          :props="treeProps"
          node-key="id"
          show-checkbox
          default-expand-all
          :filter-node-method="filterNode"
          @check="handleLeftCheckChange"
        >
        </el-tree>
      </div>
    </div>
    <!-- 中间按钮组 -->
    <div class="transfer-buttons">
      <el-button
        type="primary"
        :disabled="rightTransferDisabled"
        @click="addToLeft"
        icon="el-icon-arrow-left"
      ></el-button>
      <el-button
        type="primary"
        :disabled="leftTransferDisabled"
        @click="addToRight"
        icon="el-icon-arrow-right"
      ></el-button>
    </div>

    <!-- 右侧树形面板 -->
    <div class="transfer-panel">
      <div class="panel-header">
        <el-checkbox
          v-model="rightAllChecked"
          :indeterminate="rightIndeterminate"
          @change="handleRightAllChange"
        >
          {{ rightTitle }} ({{ rightCheckedCount }}/{{ rightTotalCount }})
        </el-checkbox>
      </div>
      <div class="panel-body">
        <el-tree
          ref="rightTree"
          :data="rightData"
          :props="treeProps"
          node-key="id"
          show-checkbox
          default-expand-all
          @check="handleRightCheckChange"
        >
        </el-tree>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'TreeTransfer',
  props: {
    // 数据源
    data: {
      type: Array,
      default: () => []
    },
    // 已选中的值
    value: {
      type: Array,
      default: () => []
    },
    // 树形配置项
    treeProps: {
      type: Object,
      default: () => ({
        children: 'children',
        label: 'label',
        disabled: 'disabled'
      })
    },
    // 左侧标题
    leftTitle: {
      type: String,
      default: '列表'
    },
    // 右侧标题
    rightTitle: {
      type: String,
      default: '已选'
    },
    // 是否显示搜索框
    filterable: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      leftData: [], // 左侧数据
      rightData: [], // 右侧数据
      leftCheckedKeys: [], // 左侧选中keys
      rightCheckedKeys: [], // 右侧选中keys
      leftAllChecked: false, // 左侧全选状态
      rightAllChecked: false, // 右侧全选状态
      leftIndeterminate: false, // 左侧半选状态
      rightIndeterminate: false // 右侧半选状态
    }
  },
  computed: {
    // 左侧总数
    leftTotalCount () {
      return this.getTotalCount(this.leftData)
    },
    // 右侧总数
    rightTotalCount () {
      return this.getTotalCount(this.rightData)
    },
    // 左侧选中数
    leftCheckedCount () {
      return this.leftCheckedKeys.length
    },
    // 右侧选中数
    rightCheckedCount () {
      return this.rightCheckedKeys.length
    },
    // 向左转移按钮是否禁用
    rightTransferDisabled () {
      return this.rightCheckedKeys.length === 0
    },
    // 向右转移按钮是否禁用
    leftTransferDisabled () {
      return this.leftCheckedKeys.length === 0
    }
  },
  watch: {
    data: {
      handler (newVal) {
        this.initData()
      },
      deep: true,
      immediate: true
    },
    value: {
      handler (newVal) {
        this.initRightData()
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    // 初始化数据
    initData () {
      // 深拷贝原始数据
      this.leftData = JSON.parse(JSON.stringify(this.data))
      this.initRightData()
    },
    // 初始化右侧数据
    initRightData () {
      if (!this.value || this.value.length === 0) {
        this.rightData = []
        return
      }

      // 从左侧数据中筛选出选中的节点
      const selectedKeys = this.value.map(item => item.id)
      this.rightData = this.getSelectedNodes(this.leftData, selectedKeys)

      // 从左侧数据中移除已选中的节点
      this.removeSelectedNodes(this.leftData, selectedKeys)

      // 设置右侧树的选中状态
      this.$nextTick(() => {
        this.$refs.rightTree.setCheckedKeys(selectedKeys)
      })
    },
    // 获取选中节点及其父节点
    getSelectedNodes (data, selectedKeys, parent = null) {
      const result = []

      data.forEach(node => {
        const newNode = { ...node }
        const hasChildren = newNode[this.treeProps.children] && newNode[this.treeProps.children].length > 0

        if (selectedKeys.includes(newNode.id)) {
          // 如果是选中的节点
          const clonedNode = JSON.parse(JSON.stringify(newNode))
          if (parent) {
            // 如果有父节点，需要保留父节点结构
            const clonedParent = JSON.parse(JSON.stringify(parent))
            clonedParent[this.treeProps.children] = [clonedNode]
            result.push(clonedParent)
          } else {
            result.push(clonedNode)
          }
        } else if (hasChildren) {
          // 如果有子节点，递归处理
          const children = this.getSelectedNodes(newNode[this.treeProps.children], selectedKeys, newNode)
          if (children.length > 0) {
            const clonedNode = JSON.parse(JSON.stringify(newNode))
            clonedNode[this.treeProps.children] = children
            result.push(clonedNode)
          }
        }
      })

      return result
    },
    // 从左侧数据中移除已选中的节点
    removeSelectedNodes (data, selectedKeys) {
      for (let i = data.length - 1; i >= 0; i--) {
        const node = data[i]
        const hasChildren = node[this.treeProps.children] && node[this.treeProps.children].length > 0

        if (selectedKeys.includes(node.id)) {
          // 如果是选中的节点，从左侧移除
          data.splice(i, 1)
        } else if (hasChildren) {
          // 如果有子节点，递归处理
          this.removeSelectedNodes(node[this.treeProps.children], selectedKeys)
          // 如果子节点全部被移除，且当前节点没有子节点了，也移除当前节点
          if (node[this.treeProps.children].length === 0) {
            data.splice(i, 1)
          }
        }
      }
    },
    // 获取树节点总数
    getTotalCount (data) {
      let count = 0
      data.forEach(node => {
        count++
        if (node[this.treeProps.children] && node[this.treeProps.children].length > 0) {
          count += this.getTotalCount(node[this.treeProps.children])
        }
      })
      return count
    },
    // 左侧树选中变化
    handleLeftCheckChange (checkedNode, checkedState) {
      this.leftCheckedKeys = checkedState.checkedKeys
      this.leftAllChecked = this.leftCheckedKeys.length === this.leftTotalCount
      this.leftIndeterminate = this.leftCheckedKeys.length > 0 && !this.leftAllChecked
    },
    // 右侧树选中变化
    handleRightCheckChange (checkedNode, checkedState) {
      this.rightCheckedKeys = checkedState.checkedKeys
      this.rightAllChecked = this.rightCheckedKeys.length === this.rightTotalCount
      this.rightIndeterminate = this.rightCheckedKeys.length > 0 && !this.rightAllChecked
    },
    // 左侧全选变化
    handleLeftAllChange (checked) {
      this.$refs.leftTree.setCheckedKeys(checked ? this.getAllKeys(this.leftData) : [])
      this.leftAllChecked = checked
      this.leftIndeterminate = false
    },
    // 右侧全选变化
    handleRightAllChange (checked) {
      this.$refs.rightTree.setCheckedKeys(checked ? this.getAllKeys(this.rightData) : [])
      this.rightAllChecked = checked
      this.rightIndeterminate = false
    },
    // 获取所有节点key
    getAllKeys (data) {
      let keys = []
      data.forEach(node => {
        keys.push(node.id)
        if (node[this.treeProps.children] && node[this.treeProps.children].length > 0) {
          keys = keys.concat(this.getAllKeys(node[this.treeProps.children]))
        }
      })
      return keys
    },
    // 添加到右侧
    addToRight () {
      if (this.leftCheckedKeys.length === 0) return

      // 获取选中的节点及其父节点
      const selectedNodes = this.getSelectedNodes(this.leftData, this.leftCheckedKeys)

      // 添加到右侧数据
      this.rightData = this.mergeTreeData(this.rightData, selectedNodes)

      // 从左侧数据中移除已选中的节点
      this.removeSelectedNodes(this.leftData, this.leftCheckedKeys)

      // 清空左侧选中状态
      this.$refs.leftTree.setCheckedKeys([])
      this.leftCheckedKeys = []
      this.leftAllChecked = false
      this.leftIndeterminate = false

      // 触发change事件
      this.emitChange()
    },
    // 添加到左侧
    addToLeft () {
      if (this.rightCheckedKeys.length === 0) return

      // 获取选中的节点及其父节点
      const selectedNodes = this.getSelectedNodes(this.rightData, this.rightCheckedKeys)

      // 添加到左侧数据
      this.leftData = this.mergeTreeData(this.leftData, selectedNodes)

      // 从右侧数据中移除已选中的节点
      this.removeSelectedNodes(this.rightData, this.rightCheckedKeys)

      // 清空右侧选中状态
      this.$refs.rightTree.setCheckedKeys([])
      this.rightCheckedKeys = []
      this.rightAllChecked = false
      this.rightIndeterminate = false

      // 触发change事件
      this.emitChange()
    },
    // 合并树数据
    mergeTreeData (target, source) {
      const result = JSON.parse(JSON.stringify(target))

      source.forEach(sourceNode => {
        const existingIndex = result.findIndex(node => node.id === sourceNode.id)
        if (existingIndex >= 0) {
          // 如果节点已存在，合并子节点
          if (sourceNode[this.treeProps.children] && sourceNode[this.treeProps.children].length > 0) {
            if (!result[existingIndex][this.treeProps.children]) {
              result[existingIndex][this.treeProps.children] = []
            }
            result[existingIndex][this.treeProps.children] = this.mergeTreeData(
              result[existingIndex][this.treeProps.children],
              sourceNode[this.treeProps.children]
            )
          }
        } else {
          // 如果节点不存在，直接添加
          result.push(sourceNode)
        }
      })

      return result
    },
    // 触发change事件
    emitChange () {
      const selectedNodes = this.$refs.rightTree.getCheckedNodes()
      this.$emit('input', selectedNodes)
      this.$emit('change', selectedNodes)
    },
    // 过滤节点
    filterNode (value, data) {
      if (!value) return true
      return data[this.treeProps.label].indexOf(value) !== -1
    }
  }
}
</script>

<style scoped>
.tree-transfer {
  display: flex;
  justify-content: center;
  align-items: center;
}

.transfer-panel {
  width: 300px;
  height: 400px;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.panel-header {
  height: 40px;
  line-height: 40px;
  background: #f5f7fa;
  padding: 0 15px;
  border-bottom: 1px solid #ebeef5;
  box-sizing: border-box;
}

.panel-body {
  flex: 1;
  overflow: auto;
  padding: 10px;
}

.transfer-buttons {
  padding: 0 20px;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

.transfer-buttons .el-button {
  margin: 10px 0;
}
</style>